package geneticOpt;
import java.util.*;
import javax.swing.JFrame;
import logic.gridSimInterface.resource.ResourceCharacteristicExt;

import logic.gridSimInterface.SimTags;
import logic.gridSimInterface.user.GridletInfo;

import gridsim.*;
import gridsim.net.SimpleLink;

import logic.schedulers.*;
import logic.schedulers.ParametersDialog;
import logicTests.Dbg;

public class GeneticOptimization implements AbstractSchedulerOptimization {
    private int cyclesCount = 10;    //pocet cyklov gen. algoritmu
    
    private geneticOptParameters paramDialog;
    private Collection<ResourceCharacteristicExt> ResourceCharacteristicExtList;
    
    public GeneticOptimization() {
        Dbg.vypis("Konstruktor GeneticOptimization()");
    }

    public void createParamDialog(JFrame owner) {
        paramDialog = new geneticOptParameters(owner);
    }

    /**
     * nazov algoritmu, ktory sa bude zobrazovat v menu
     */
    public String getName() {
        return "Genetic algorithm";
    }

    public int getIndex() {
        return 10;
    }
    
    /**
     * ak je pre algoritmus mozne nastavit nejake parametre, tak vracia odkaz na 
     *    dialogove okno, ktore sa moze otvorit
     */
    public ParametersDialog getParamDialog() {
        return paramDialog;
    }

    /**
     * ak planovac obsahuje parametre, tak ich vrati - potrebne pre nastavovaci 
     *    dialog na inicializaciu
     */
    public Object getParameters() {
        return new Integer(cyclesCount);
    }

    /**
     * nastavi si dane parametre
     */
    public void setParameters(Object parameters) {
        cyclesCount = ((Integer)parameters);
    }


    public Vector<String> getParametersText() {
        Vector<String> params = new Vector<String>();
        params.add(Integer.toString(cyclesCount));
        return params;
    }

    public void setParametersText(Vector<String> newValues) {
        cyclesCount = Integer.parseInt(newValues.get(0));
    }

    public void optimizeDynamic(int costFunction, Collection<ResourceCharacteristicExt> ResourceCharacteristicExtList){
        this.ResourceCharacteristicExtList = ResourceCharacteristicExtList;
        CostFunction.init(costFunction, ResourceCharacteristicExtList);
        Dbg.vypis("Strarting genetic optimization with cyclesCoutn = " 
                  + cyclesCount + "...");
        geneticAlgorithm(cyclesCount);
    }
    
    public void optimizeStatic(int costFunction, Collection<ResourceCharacteristicExt> resourceInfoList, Collection<GridletInfo> gridlets) {
        ResourceCharacteristicExt firstRes = resourceInfoList.iterator().next();
        for (GridletInfo elem : gridlets) {
            firstRes.addGridletInfoToScheduleList(elem);
        }
        optimizeDynamic(costFunction, resourceInfoList);
    }

    
    ////////////////////////////////////////////////////////////////////////////
    //////////////////////// PREVZATE METODY ///////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    
    /**
     * Find resource in broker ResourceCharacteristicExtList and returns its ResourceCharacteristicExt
     * @param resourceId id of desired resource
     * @return ResourceCharacteristicExt of desired resource, null if resource with given ID not found
     */
    protected ResourceCharacteristicExt getResource(int resourceId) {
    	for (Iterator iter = ResourceCharacteristicExtList.iterator(); iter.hasNext();) {
			ResourceCharacteristicExt element = (ResourceCharacteristicExt) iter.next();
			if(element.getResourceID() == resourceId)
				return element;
		}
    	return null;
    }
    
     
    private void geneticAlgorithm(int cycles) {
    	//filling of variable with all grildets that are scheduled on all resources
    	double oldScheduleTime = CostFunction.calculateScheduleCost(-1, -1, null);
    	HashMap allScheduledGriletInfos = new HashMap();
    	int[] resourceArray = new int[ResourceCharacteristicExtList.size()];
    	int key = 0;
    	int res = 0;
    	for (Iterator iter = ResourceCharacteristicExtList.iterator(); iter.hasNext();) {
			ResourceCharacteristicExt ResourceCharacteristicExt = (ResourceCharacteristicExt) iter.next();
			resourceArray[res++] = ResourceCharacteristicExt.getResourceID();     //array of resources containing resources ID 
			for (Iterator iterator = ResourceCharacteristicExt.getScheduledGridlets().iterator(); iterator.hasNext();) {
				GridletInfo gridletInfo = (GridletInfo) iterator.next();
				allScheduledGriletInfos.put(new Integer(key++), gridletInfo);  //hashMap of all scheduled gridlets
			}			
		}
    	
    	double[][] population = new double[100][allScheduledGriletInfos.size()];
    	double[][] mutatedPopulation = new double[1000][allScheduledGriletInfos.size()];
    	//creating starting population of 100 individuals
    	//each individual is represented as a vector (a1,a2,a3....aN) where aN is ID of resource and N is key for gridletInfo in HashMap
    	
    	//first individual represents actual schedule
    	for(int i = 0 ; i < allScheduledGriletInfos.size(); i++) {
    		population[0][i] = ((GridletInfo)allScheduledGriletInfos.get(new Integer(i))).getResourceId();
    		mutatedPopulation[0][i] = ((GridletInfo)allScheduledGriletInfos.get(new Integer(i))).getResourceId();
    	}
    	
    	Random random = new Random(11);
    	for(int i = 1; i < 100; i++) {
    		for(int j = 0 ; j < allScheduledGriletInfos.size(); j++) {
    			LinkedList<ResourceCharacteristicExt> suitableResources = new LinkedList<ResourceCharacteristicExt>();
    			for (Iterator iter = ResourceCharacteristicExtList.iterator(); iter.hasNext();) {
					ResourceCharacteristicExt element = (ResourceCharacteristicExt) iter.next();
					if( element.isSuitableResource((GridletInfo) allScheduledGriletInfos.get(new Integer(j))) )
						suitableResources.add(element);
				}
    			
    			int resource = ((ResourceCharacteristicExt)suitableResources.get(random.nextInt(suitableResources.size()))).getResourceID();
    			population[i][j] =  resource;    			
    			mutatedPopulation[i][j] = resource;
    		}
    	}
    	
    	//creating of new population throught crossover and mutation
    	for(int round = 0; round < cycles; round++) {
    		int mutation = random.nextInt(300) + 100; //100 to 400 mutation, rest will by done by crossover 
    		//mutating
    		for(int i = 100; i < mutation; i++) {
    			int individual = random.nextInt(population.length);
    			for (int j = 0; j < population[individual].length; j++) {
    				mutatedPopulation[i][j] = population[individual][j];
				}

    			int mutationCount = random.nextInt(allScheduledGriletInfos.size() + 1); //can be chanched to 1 to allow only one mutation in an individual
    			for(int j = 0; j < mutationCount; j++) {
    				int index = random.nextInt(allScheduledGriletInfos.size());
        			LinkedList<ResourceCharacteristicExt> suitableResources = new LinkedList<ResourceCharacteristicExt>();
        			for (Iterator iter = ResourceCharacteristicExtList.iterator(); iter.hasNext();) {
    					ResourceCharacteristicExt element = (ResourceCharacteristicExt) iter.next();
    					if( element.isSuitableResource((GridletInfo) allScheduledGriletInfos.get(new Integer(j))) )
    						suitableResources.add(element);
    				}
    				mutatedPopulation[i][index] = ((ResourceCharacteristicExt)suitableResources.get(random.nextInt(suitableResources.size()))).getResourceID();
    			}    			
    		}
    		//crossingover
    		for(int i = mutation; i < 1000; i++) {
    			int cross = random.nextInt(allScheduledGriletInfos.size() + 1);
    			int firstParent = random.nextInt(population.length);
    			int secondParent = random.nextInt(population.length);
    			while(firstParent == secondParent)
    				secondParent = random.nextInt(population.length);
    			for(int j = 0; j < cross; j++)
    				mutatedPopulation[i][j] = population[firstParent][j];
    			for(int j = cross; j < allScheduledGriletInfos.size(); j++)
    				mutatedPopulation[i][j] = population[secondParent][j];
    		}
    		

    		//counting max execution time for schedules in population and adding them to backupPopulation
    		double[][] backupPopulation = new double[1100][allScheduledGriletInfos.size() + 1];
    		for(int i = 0; i < population.length; i++) {
    			for(int j = 0; j < population[i].length; j++)
    				backupPopulation[i][j] = population[i][j];    			
    			backupPopulation[i][allScheduledGriletInfos.size()] = CostFunction.calculatePopulationCost(resourceArray, allScheduledGriletInfos, backupPopulation[i]);
    		}
    		
    		//counting max execution time for schedules in mutatedPopulation and adding them to backupPopulation    		
    		for(int i = population.length; i < mutatedPopulation.length + population.length; i++) {
    			for(int j = 0; j < mutatedPopulation[i - population.length].length; j++)
    				backupPopulation[i][j] = mutatedPopulation[i - population.length][j];    			
    			backupPopulation[i][allScheduledGriletInfos.size()] = CostFunction.calculatePopulationCost(resourceArray, allScheduledGriletInfos, backupPopulation[i]);
    		}
    		

    		//sorting backupPopulation
    		
    		for(int i = 0; i < population.length; i++) {
    			double minTime = Double.MAX_VALUE;
    			int index = -1;
    			for(int j = 0; j < backupPopulation.length; j++) {
    				if(minTime > backupPopulation[j][allScheduledGriletInfos.size()]) {
    					minTime = backupPopulation[j][allScheduledGriletInfos.size()];
    					index = j;
    				}
    			}
    			
    			if(index == -1) {
    				System.out.println("Error: cannot find min time in populations");
    				return;
    			}    				
    			
    			backupPopulation[index][allScheduledGriletInfos.size()] = Double.MAX_VALUE; //penalty value ensuring that schedule will be chosen only once
    			for(int j = 0; j < population[i].length; j++)
    				population[i][j] = backupPopulation[index][j];
    		}    		
    	}
    	
    	
    	double newScheduleTime = CostFunction.calculatePopulationCost(resourceArray, allScheduledGriletInfos, population[0]);
    	if(newScheduleTime < oldScheduleTime) { // better shcedule find, rescheduling ...
    		//clearing schedule
    		for (Iterator iter = ResourceCharacteristicExtList.iterator(); iter.hasNext();) {
				ResourceCharacteristicExt ResourceCharacteristicExt = (ResourceCharacteristicExt) iter.next();
				ResourceCharacteristicExt.clearScheduledGridlets();				
			}
    		//adding all gridletInfos from allScheduledGriletInfos to resources (population[0].length == allScheduledGriletInfos.size())
    		for (int i = 0; i < population[0].length; i++) {
    			ResourceCharacteristicExt ResourceCharacteristicExt = getResource( (new Double(population[0][i])).intValue());
    			ResourceCharacteristicExt.addGridletInfoToScheduleList((GridletInfo) allScheduledGriletInfos.get(new Integer(i)));
			}
    		System.out.println("Genetic algorithm found a better schedule with time " + newScheduleTime + " compared to old schedule time " + oldScheduleTime);
    	}    		
    	else
    		System.out.println("Genetic alogrithm didn't find better schedule");
    }

}
